'''
写个python 图形界面程序，界面中可以选择输入的CSV 文件，然后读取文件内容，将CSV 文件第三列的十六进制数字解析为单色bitmap，尺寸为64 x 64 像素，然后绘制在界面上。bitmap 的结构是：水平方向一共64 列，每8 行像素分成一页，一页中的一列8 个像素点用一字节表示，二进制位1 表示黑色，0 表示白色。MSB 对应的像素点在下方。比如二进制0b10101010，就表示一列八个黑白像素交替。CSV 解析规则是：CSV 中连续三行分别为B0、12、2 表示第0 页显示数据的开头，之后跟随64 行1 字节的显示数据，然后用一行EE 结尾。B1 12 2 则表示第1 页显示数据的开头，以此类推，最大到B7 12 2，即第7 页。8页数据在CSV 文件中包含连续排列，之后可能还有另一个8页的数据，但是一次只显示一个8页，当用户按键更新后，再向后解析并显示。CSV 文件的示例：78,10413520,EE
79,10419480,B1
80,10424740,12
81,10429980,2
82,10435520,0
83,10440940,0
84,10446360,0
85,10451780,0
86,10457180,0
87,10462600,0
88,10468020,0
89,10473440,0
90,10478860,0
91,10484280,0
92,10489680,0
93,10495100,0
...
146,10782300,EE
'''

import tkinter as tk
from tkinter import filedialog, messagebox
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
import re

class CSVBitmapViewer:
    def __init__(self, root):
        self.root = root
        self.root.title("CSV Bitmap Viewer")
        self.root.geometry("800x600")
        
        # 设置中文字体
        #plt.rcParams["font.family"] = ["SimHei", "WenQuanYi Micro Hei", "Heiti TC"]
        plt.rcParams["font.family"] = ["SimHei"]
        
        # 数据和状态
        self.csv_data = None
        self.current_bitmap_index = 0
        self.bitmaps = []
        
        # 创建界面组件
        self.create_widgets()
        
    def create_widgets(self):
        # 顶部按钮区域
        button_frame = tk.Frame(self.root)
        button_frame.pack(fill=tk.X, padx=10, pady=10)
        
        # 选择文件按钮
        self.select_file_btn = tk.Button(button_frame, text="选择CSV文件", command=self.select_csv_file)
        self.select_file_btn.pack(side=tk.LEFT, padx=5)
        
        # 上一个和下一个按钮
        self.prev_btn = tk.Button(button_frame, text="上一个", command=self.show_previous_bitmap, state=tk.DISABLED)
        self.prev_btn.pack(side=tk.LEFT, padx=5)
        
        self.next_btn = tk.Button(button_frame, text="下一个", command=self.show_next_bitmap, state=tk.DISABLED)
        self.next_btn.pack(side=tk.LEFT, padx=5)
        
        # 状态标签
        self.status_var = tk.StringVar()
        self.status_var.set("请选择CSV文件")
        self.status_label = tk.Label(button_frame, textvariable=self.status_var)
        self.status_label.pack(side=tk.LEFT, padx=20)
        
        # 图像显示区域
        self.figure, self.ax = plt.subplots(figsize=(6, 6))
        self.canvas = FigureCanvasTkAgg(self.figure, master=self.root)
        self.canvas.get_tk_widget().pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
        
        # 底部信息区域
        self.info_text = tk.Text(self.root, height=5, wrap=tk.WORD)
        self.info_text.pack(fill=tk.X, padx=10, pady=5)
        self.info_text.config(state=tk.DISABLED)
    
    def select_csv_file(self):
        file_path = filedialog.askopenfilename(
            filetypes=[("CSV文件", "*.csv"), ("所有文件", "*.*")]
        )
        
        if file_path:
            try:
                self.csv_data = pd.read_csv(file_path)
                self.parse_csv_data()
                if self.bitmaps:
                    self.current_bitmap_index = 0
                    self.display_current_bitmap()
                    self.update_button_states()
                    self.status_var.set(f"已加载 {len(self.bitmaps)} 个bitmap")
                else:
                    messagebox.showinfo("提示", "CSV文件中未找到有效的bitmap数据")
                    self.status_var.set("未找到有效数据")
            except Exception as e:
                messagebox.showerror("错误", f"加载文件时出错: {str(e)}")
                self.status_var.set("加载文件失败")
    
    def parse_csv_data(self):
        """解析CSV数据，提取所有bitmap（每个bitmap包含8页数据）"""
        self.bitmaps = []
        if self.csv_data is None:
            return
        
        rows = self.csv_data.values.tolist()
        i = 0
        total_rows = len(rows)
        
        while i < total_rows:
            # 查找B0标记作为一个bitmap的开始
            if i + 2 < total_rows:
                current_row = rows[i]
                if len(current_row) >= 3 and isinstance(current_row[2], str) and current_row[2] == 'B0':
                    # 检查接下来的两行是否是12和2
                    if (int(rows[i+1][2]) == 12) and (int(rows[i+2][2]) == 2):
                        
                        # 找到一个可能的bitmap起始点，尝试解析完整的8页
                        bitmap_data = np.zeros((64, 64), dtype=np.uint8)
                        pages_loaded = 0
                        current_position = i
                        
                        while pages_loaded < 8 and current_position < total_rows:
                            # 检查当前页标记
                            page_row = rows[current_position]
                            if len(page_row) < 3 or not isinstance(page_row[2], str):
                                break
                            
                            page_match = re.match(r'B(\d)', page_row[2])
                            if not page_match:
                                break
                            
                            page_num = int(page_match.group(1))
                            if page_num != pages_loaded:
                                # 期望的页号不匹配，可能不是完整的bitmap
                                break
                            
                            # 检查接下来的两行是否是12和2
                            if (current_position + 2 >= total_rows) or (int(rows[current_position+1][2]) != 12) and (int(rows[current_position+2][2]) != 2):
                                break
                            
                            # 解析当前页的数据
                            current_position += 3  # 跳过Bx, 12, 2
                            page_data_count = 0
                            
                            while current_position < total_rows and page_data_count < 64:
                                data_row = rows[current_position]
                                if len(data_row) < 3:
                                    current_position += 1
                                    continue
                                
                                try:
                                    # 尝试将第三列解析为十六进制
                                    hex_str = str(data_row[2])
                                    if hex_str.startswith('0x'):
                                        byte_value = int(hex_str, 16)
                                    else:
                                        byte_value = int(hex_str, 16)
                                    
                                    # 计算在bitmap中的位置
                                    col = page_data_count % 64
                                    # 计算行位置，每页8行
                                    row_pos = page_num * 8
                                    
                                    # 将字节转换为8个像素
                                    for bit in range(8):
                                        #pixel_value = (byte_value >> (7 - bit)) & 1
                                        #pixel_value = ((byte_value << bit) & 0x80) >> 7
                                        pixel_value = ((byte_value >> bit) & 0x01)
                                        # 注意MSB在上方
                                        bitmap_data[row_pos + bit, col] = pixel_value
                                    
                                    page_data_count += 1
                                    current_position += 1
                                except ValueError:
                                    # 如果无法解析为十六进制，可能是结束标记或其他内容
                                    break
                            
                            # 检查是否有结束标记EE
                            if current_position < total_rows and len(rows[current_position]) >= 3 and str(rows[current_position][2]) == 'EE':
                                current_position += 1
                            
                            pages_loaded += 1
                        
                        # 如果成功加载了8页，添加到bitmaps列表
                        if pages_loaded == 8:
                            self.bitmaps.append(bitmap_data)
                            i = current_position
                            continue
                        else:
                            # 未找到完整的8页，继续搜索
                            i += 1
                    else:
                        # 不是有效的bitmap起始，继续查找
                        i += 1
                else:
                    # 不是B0行，继续查找
                    i += 1
            else:
                # 剩余行数不足，退出循环
                break
    
    def display_current_bitmap(self):
        """显示当前选中的bitmap"""
        if 0 <= self.current_bitmap_index < len(self.bitmaps):
            bitmap = self.bitmaps[self.current_bitmap_index]
            
            # 清除之前的图像
            self.ax.clear()
            
            # 显示bitmap
            self.ax.imshow(bitmap, cmap='gray', interpolation='nearest')
            self.ax.set_title(f"Bitmap {self.current_bitmap_index + 1}/{len(self.bitmaps)} (完整64x64像素)")
            self.ax.set_xlabel("列")
            self.ax.set_ylabel("行")
            
            # 设置坐标轴范围
            self.ax.set_xlim([0, 64])
            self.ax.set_ylim([64, 0])  # 图像原点在左上角
            
            # 设置网格线
            self.ax.set_xticks(np.arange(-0.5, 64, 1))
            self.ax.set_yticks(np.arange(-0.5, 64, 8))
            
            x_label = [0, ]
            for i in range(10, 80, 10):
                x_label.extend(['', 2, '', 4, '', 6, '', 8, ''])
                x_label.append(i)
                
            x_label = x_label[0:65]
            self.ax.set_xticklabels(x_label)
            self.ax.set_yticklabels(range(0, 9))
            self.ax.grid(color='gray', linestyle='-', linewidth=1)
            
            # 更新画布
            self.figure.canvas.draw()
            
            # 更新信息区域
            self.info_text.config(state=tk.NORMAL)
            self.info_text.delete(1.0, tk.END)
            self.info_text.insert(tk.END, f"显示第 {self.current_bitmap_index + 1} 个bitmap (共 {len(self.bitmaps)} 个)\n")
            self.info_text.insert(tk.END, f"数据大小: {bitmap.shape[0]}x{bitmap.shape[1]} 像素\n")
            self.info_text.insert(tk.END, f"黑色像素比例: {np.sum(bitmap) / (bitmap.size) * 100:.2f}%")
            self.info_text.config(state=tk.DISABLED)
    
    def update_button_states(self):
        """更新按钮状态"""
        if len(self.bitmaps) <= 1:
            self.prev_btn.config(state=tk.DISABLED)
            self.next_btn.config(state=tk.DISABLED)
        else:
            self.prev_btn.config(state=tk.NORMAL if self.current_bitmap_index > 0 else tk.DISABLED)
            self.next_btn.config(state=tk.NORMAL if self.current_bitmap_index < len(self.bitmaps) - 1 else tk.DISABLED)
    
    def show_previous_bitmap(self):
        """显示上一个bitmap"""
        if self.current_bitmap_index > 0:
            self.current_bitmap_index -= 1
            self.display_current_bitmap()
            self.update_button_states()
    
    def show_next_bitmap(self):
        """显示下一个bitmap"""
        if self.current_bitmap_index < len(self.bitmaps) - 1:
            self.current_bitmap_index += 1
            self.display_current_bitmap()
            self.update_button_states()

if __name__ == "__main__":
    root = tk.Tk()
    app = CSVBitmapViewer(root)
    root.mainloop()    